#include "EngineHeader.h"
#include "..\Graphics\GraphicsHeader.h"
#include "blp.h"
#include "../jpeg/jpeg.h"


LPDIRECT3DTEXTURE9 LoadBLP(LPDIRECT3DDEVICE9 pd3dDevice, LPSTR lpszFileName, int *pPicType)
{
	assert( pd3dDevice && lpszFileName);	

	LPDIRECT3DTEXTURE9 pTexture = NULL;
	SBlpHeader *pBlpHeader = NULL;
	byte *pbyBlpData = NULL;
	byte *pbyBlpDataPtr = NULL;
	byte *pImage = NULL;
    uint uJpegHeaderSize = 0;
	uint uJpegSize = 0;
    byte *pbyJpegData = NULL;
	int width = 0, height = 0;
	int linebytesDes = 0;

	
	FILE *fp;
	int len;

	if(strlen(lpszFileName) == 0)
		return FALSE;

	fp = fopen(lpszFileName, "rb");
	if(!fp)
		return FALSE;

	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	pbyBlpData = new byte[len];
	if(!pbyBlpData)
	{
		fclose(fp);
		goto ON_ERROR;
	}
	
	fseek(fp, 0, SEEK_SET);
	fread(pbyBlpData, 1, len, fp);
	fclose(fp);
	
    // Get the Blp file header and check it.
    pbyBlpDataPtr = pbyBlpData;
    pBlpHeader = (SBlpHeader *)pbyBlpDataPtr;
    if (memcmp (pBlpHeader->szTag, "BLP1", 4) != 0)
    {
        goto ON_ERROR;
    }

	if(pPicType)
	{
		*pPicType = pBlpHeader->uPicType;
	}

    pbyBlpDataPtr += sizeof (SBlpHeader);
	
    // There are 2 types of blp files: paletted and jpeg.
    if (pBlpHeader->uCompress == E_BDT_JPEG)
    {

        // Get the jpeg header size.
        memcpy (&uJpegHeaderSize, pbyBlpDataPtr, sizeof (uint));
        pbyBlpDataPtr += sizeof (uJpegHeaderSize);

        // Allocate temporary spaces for jpeg decoding.
        pbyJpegData = new byte [uJpegHeaderSize + pBlpHeader->uMipmapSize [0]];
        if (pbyJpegData == NULL)
        {
            goto ON_ERROR;
        }

        // Get jpeg header.
        memcpy (pbyJpegData, pbyBlpDataPtr, uJpegHeaderSize);

        // Get jpeg data.
        pbyBlpDataPtr = pbyBlpData + pBlpHeader->uMipmapOffset [0];
        memcpy (pbyJpegData + uJpegHeaderSize, pbyBlpDataPtr, pBlpHeader->uMipmapSize [0]);

		uJpegSize = uJpegHeaderSize + pBlpHeader->uMipmapSize [0];

		width = pBlpHeader->uSizeX;
		height = pBlpHeader->uSizeY;

		if(FAILED(D3DXCreateTexture(pd3dDevice,
									width, 
									height, 
									D3DX_DEFAULT, 
									0,
									D3DFMT_UNKNOWN, 
									D3DPOOL_MANAGED, 
									&pTexture)))
		{
			//TRACE("texture creation failed!\n");
			return FALSE;
		}

		UCHAR *DecodeBuffer = NULL;
		DecodeBuffer = new UCHAR[width * height * 4];
		if(!DecodeBuffer)
		{
            goto ON_ERROR;
		}

		if(!JPEG::Read(pbyJpegData, uJpegSize, DecodeBuffer, width * height * 4))
		{
			//TRACE("Jpeg Read Fail \n");
			return FALSE;
		}

		D3DLOCKED_RECT LockedRect;

		if(FAILED(pTexture->LockRect(0, &LockedRect, NULL, D3DLOCK_DISCARD)))
		{
			//TRACE("texture locking failed!\n");
			return FALSE;
		}

		int Index = 0;
		int BufferIndex = 0;
		UCHAR * Pointer = reinterpret_cast<UCHAR*>(LockedRect.pBits);

		for(int Y = 0; Y < static_cast<INT>(height); Y++)
		{
			for(int X = 0; X < static_cast<INT>(width); X++)
			{
				Pointer[Index++] = DecodeBuffer[BufferIndex++];
				Pointer[Index++] = DecodeBuffer[BufferIndex++];
				Pointer[Index++] = DecodeBuffer[BufferIndex++];
				Pointer[Index++] = DecodeBuffer[BufferIndex++];
			}

			Index += LockedRect.Pitch - (width * 4);
		}

		pTexture->UnlockRect(0);

		delete [] DecodeBuffer;

		//D3DXFilterTexture(pTexture, NULL, D3DX_DEFAULT, D3DX_DEFAULT);
    }
    else if(pBlpHeader->uCompress == E_BDT_PALETTED)
    {
		BLP_RGBA palette[PALETTE_SIZE];
		BLP_PIXEL *pSource;
		int size;
		byte *pTemp = NULL;

		pbyBlpDataPtr = pbyBlpData + sizeof(SBlpHeader);
		memcpy(palette, pbyBlpDataPtr, sizeof(palette));

		switch(pBlpHeader->uPicType)
		{
		case 3:
		case 4:
			{
				pbyBlpDataPtr = pbyBlpData + pBlpHeader->uMipmapOffset [0];
				pSource = (BLP_PIXEL *)pbyBlpDataPtr;
				
				width = pBlpHeader->uSizeX;
				height = pBlpHeader->uSizeY;
				linebytesDes = width * 4;
				size = linebytesDes * height;
				pTemp = new BYTE[size];
				if(!pTemp)
				{
					goto ON_ERROR;
				}
				
				int n = width * height;
				for(int i = 0; i<n; i++)
				{
					pTemp[i * 4 + 0] = palette[pSource[i].Index].Red;
					pTemp[i * 4 + 1] = palette[pSource[i].Index].Green;
					pTemp[i * 4 + 2] = palette[pSource[i].Index].Blue;
				}
				
				for(int i = 0; i<n; i++)
				{
					pTemp[i * 4 + 3] = n + pSource[i].Index;
				}
			}
			break;
		case 5:
			{
				pbyBlpDataPtr = pbyBlpData + pBlpHeader->uMipmapOffset [0];
				pSource = (BLP_PIXEL *)pbyBlpDataPtr;
				
				width = pBlpHeader->uSizeX;
				height = pBlpHeader->uSizeY;
				linebytesDes = width * 4;
				size = linebytesDes * height;
				pTemp = new BYTE[size];
				if(!pTemp)
				{
					goto ON_ERROR;
				}
				
				int n = width * height;
				for(int i = 0; i<n; i++)
				{
					pTemp[i * 4 + 0] = palette[pSource[i].Index].Red;
					pTemp[i * 4 + 1] = palette[pSource[i].Index].Green;
					pTemp[i * 4 + 2] = palette[pSource[i].Index].Blue;
					pTemp[i * 4 + 3] = 255 - palette[pSource[i].Index].Alpha;
				}
			}
			break;
		default:
			{
				//TRACE("Unsupport picture type\n");
				assert(FALSE);
				if(pTemp)
				{
					delete [] pTemp;
					pTemp = NULL;
				}
				goto ON_ERROR;
			}
			break;
		}

		D3DXCreateTexture(pd3dDevice, 
						  width, 
						  height, 
						  D3DX_DEFAULT, 
						  0,
						  D3DFMT_UNKNOWN,
						  D3DPOOL_MANAGED, 
						  &pTexture);
		//ASSERT(pTexture);

		D3DLOCKED_RECT LockedRect;
		int Index = 0;
		int BufferIndex = 0;
		UCHAR* Pointer = NULL;
		int x, y;

		pTexture->LockRect(0, &LockedRect, NULL, 0);
		Pointer = (UCHAR *)LockedRect.pBits;

		for(y = 0; y < height; y++)
		{
			for(x = 0; x < width; x++)
			{
				Pointer[Index++] = pTemp[BufferIndex++];
				Pointer[Index++] = pTemp[BufferIndex++];
				Pointer[Index++] = pTemp[BufferIndex++];
				Pointer[Index++] = pTemp[BufferIndex++];
			}
			Index += LockedRect.Pitch - width * 4;
		}

		pTexture->UnlockRect(0);

		if(pTemp)
		{
			delete [] pTemp;
			pTemp = NULL;
		}

		D3DXFilterTexture(pTexture, NULL, D3DX_DEFAULT, D3DX_DEFAULT);
	}
	else
	{
		// Unsupported blp data type: invalid!
		//ASSERT(FALSE);
        goto ON_ERROR;
    }

    // Clean up.
	if(pbyBlpData)
	{
		delete [] pbyBlpData;
		pbyBlpData = NULL;
	}

	if(pbyJpegData)
	{
		delete [] pbyJpegData;
		pbyJpegData = NULL;
	}

    // Success.
    return pTexture;

    // Error handle.
ON_ERROR:

	if(pbyBlpData)
		delete [] pbyBlpData;

	if(pbyJpegData)
		delete [] pbyJpegData;

    return NULL;
}
